/*

utility functions

*/

#include "stdafx.h"
#include "utils.h"

#define ALERT_MESSAGE_SIZE 0x400
char AlertMessage[ALERT_MESSAGE_SIZE];

int alert(const char* msg, ...) {
	int ret;
	
	va_list argptr;
	va_start(argptr, msg);
	ret = valert(0, NULL, msg, argptr);
	va_end(argptr);

	return ret;
}

int alert(unsigned int flags, const char* title, const char* msg, ...) {
	int ret;

	va_list argptr;
	va_start(argptr, msg);
	ret = valert(flags, title, msg, argptr);
	va_end(argptr);

	return ret;
}

int alert(const wchar_t* msg, ...) {
	int ret;

	char* utf8Msg = wide_2_utf8(msg);
	va_list argptr;
	va_start(argptr, msg);
	ret = valert(0, NULL, utf8Msg, argptr);
	va_end(argptr);

	// caller is responsible for freeing memory after wide_2_utf8
	free((void*)utf8Msg);

	return ret;
}

int alert(unsigned int flags, const wchar_t* title, const wchar_t* msg, ...) {
	int ret;
	
	char* utf8Title;
	if (title != NULL)
			utf8Title = wide_2_utf8(title);
	char* utf8Msg = wide_2_utf8(msg);
	va_list argptr;
	va_start(argptr, msg);
	ret = valert(flags, utf8Title, utf8Msg, argptr);
	va_end(argptr);

	// caller is responsible for freeing memory after wide_2_utf8
	free((void*)utf8Msg);
	if (title != NULL)
		free((void*)utf8Title);

	return ret;
}

/*
a lot of %s params are expected, and vswprintf does not convert them properly (why?)
so we convert everything to utf8 and use vsnprintf instead
but then we convert to wide for actual display
so when a wide is passed to alert, it converts it utf8 and then back again
it's ugly, but it does what we want
*/
int valert(unsigned int flags, const char* title, const char* msg, va_list vargs) {
	wchar_t* wideTitle = L"PlPN";
	if (title != NULL)
		wideTitle = utf8_2_wide(title);
	vsnprintf(AlertMessage, ALERT_MESSAGE_SIZE, msg, vargs);
	wchar_t* wideMsg = utf8_2_wide(AlertMessage);

	int ret = MessageBoxW(NULL, wideMsg, wideTitle, flags);

	// caller is responsible for freeing memory after wide_2_utf8
	free((void*)wideMsg);
	if (title != NULL)
		free((void*)wideTitle);

	return ret;
}

char* wide_2_utf8(const wchar_t* wide, int target_length) {
	if (wide == NULL)
		return NULL;
	
	char* utf8 = NULL;
	char alertMsg[50];
	
	if (target_length == -1) {
		target_length = WideCharToMultiByte(
			CP_UTF8,		// code page
			0,				// flags (must be set to 0 or WC_ERR_INVALID_CHARS)
			wide,			// in
			-1,				// size of in (-1 means null-terminated)
			utf8,			// out
			0,				// size of out (0 means calculate required size, but do not convert)
			NULL,			// which default character to use
			NULL			// upon return, indicates whether the default character was used
		);
		if (target_length == 0) {
			DWORD err = GetLastError();
			alert("Unable to calculate length; unconverted name follows reason for failure");
			switch (err) {
				case ERROR_INSUFFICIENT_BUFFER:
					alert("Insufficient buffer");
					break;
				case ERROR_INVALID_FLAGS:
					alert("Invalid flags");
					break;
				case ERROR_INVALID_PARAMETER:
					alert("Invalid parameter");
					break;
				case ERROR_NO_UNICODE_TRANSLATION:
					alert("Invalid unicode");
					break;
				default:
					alert("Unknown error code: %d", GetLastError());
					break;
			}
			// don't use alert, it may call us and cause a loop
			MessageBoxW(NULL, wide, L"PlPN", 0);
		}
	}
	
	utf8 = (char*)malloc(target_length);
	
	int success = WideCharToMultiByte(
		CP_UTF8,		// code page
		0,				// flags (must be set to 0 or WC_ERR_INVALID_CHARS)
		wide,			// in
		-1,				// size of in (-1 means null-terminated)
		utf8,			// out
		target_length,	// size of out (0 means calculate required size, but do not convert)
		NULL,			// which default character to use
		NULL			// upon return, indicates whether the default character was used
	);
	if (success == 0) {
		DWORD err = GetLastError();
		alert("Unable to convert name; unconverted name follows reason for failure");
		switch (err) {
			case ERROR_INSUFFICIENT_BUFFER:
				alert("Insufficient buffer");
				break;
			case ERROR_INVALID_FLAGS:
				alert("Invalid flags");
				break;
			case ERROR_INVALID_PARAMETER:
				alert("Invalid parameter");
				break;
			case ERROR_NO_UNICODE_TRANSLATION:
				alert("Invalid unicode");
				break;
			default:
				alert("Unknown error code: %d", GetLastError());
				break;
		}
		// don't use alert, it may call us and cause a loop
		MessageBoxW(NULL, wide, L"PlPN", 0);
	}
	
	return utf8;
}

wchar_t* utf8_2_wide(const char* utf8, int target_length) {
	if (utf8 == NULL)
		return NULL;
	
	wchar_t* wide = NULL;
	char alertMsg[50];
	
	if (target_length == -1) {
		target_length = MultiByteToWideChar(
			CP_UTF8,		// code page
			0,				// flags (must be set to 0 for UTF8)
			utf8,			// in
			-1,				// size of in (-1 means null-terminated)
			wide,			// out
			0				// size of out (0 means calculate required size, but do not convert)
		);
		if (target_length == 0) {
			DWORD err = GetLastError();
			alert("Unable to convert name; unconverted name follows reason for failure");
			switch (err) {
				case ERROR_INSUFFICIENT_BUFFER:
					alert("Insufficient buffer");
					break;
				case ERROR_INVALID_FLAGS:
					alert("Invalid flags");
					break;
				case ERROR_INVALID_PARAMETER:
					alert("Invalid parameter");
					break;
				case ERROR_NO_UNICODE_TRANSLATION:
					alert("Invalid unicode");
					break;
				default:
					sprintf(alertMsg, "Unknown error code: %d", GetLastError());
					alert(alertMsg);
					break;
			}
			// don't use alert, it may call us and cause a loop
			MessageBoxA(NULL, utf8, "PlPN", 0);
			return NULL;
		}
	}
	
	wide = (wchar_t*)malloc(target_length * sizeof(wchar_t));
	
	int success = MultiByteToWideChar(
		CP_UTF8,		// code page
		0,				// flags (must be set to 0 for UTF8)
		utf8,			// in
		-1,				// size of in (-1 means null-terminated)
		wide,			// out
		target_length	// size of out (0 means calculate required size, but do not convert)
	);
	if (target_length == 0) {
		DWORD err = GetLastError();
		alert("Unable to convert name; unconverted name follows reason for failure");
		switch (err) {
			case ERROR_INSUFFICIENT_BUFFER:
				alert("Insufficient buffer");
				break;
			case ERROR_INVALID_FLAGS:
				alert("Invalid flags");
				break;
			case ERROR_INVALID_PARAMETER:
				alert("Invalid parameter");
				break;
			case ERROR_NO_UNICODE_TRANSLATION:
				alert("Invalid unicode");
				break;
			default:
				sprintf(alertMsg, "Unknown error code: %d", GetLastError());
				alert(alertMsg);
				break;
		}
		// don't use alert, it may call us and cause a loop
		MessageBoxA(NULL, utf8, "PlPN", 0);
	}
	
	return wide;
}
